package com.yy.tech.data.mapper.introspector;

import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.util.Converter;
import com.yy.tech.data.annotation.SecretProperty;
import com.yy.tech.data.coder.decode.Decoder;
import com.yy.tech.data.coder.encode.Encoder;
import com.yy.tech.data.context.DesensitizeContext;
import com.yy.tech.data.provider.model.FindCondition;
import com.yy.tech.data.provider.model.SecretRule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author yy
 * @date 2022/4/5
 * @description: TODO
 */
@Component
public class DesensitizeAnnotationIntrospector extends JacksonAnnotationIntrospector{

    private Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private DesensitizeContext context;

    @Override
    public Object findSerializationConverter(final Annotated annotated) {
        SecretProperty secretProperty = findSecretProperty(annotated);
        if (null != secretProperty){
            return context.getHttpEncoder(secretProperty.encoderClass(),secretProperty.encoder());
        }
        Encoder encoder = findEncoder(annotated);
        if (null != encoder){
            return encoder;
        }
        return super.findSerializationConverter(annotated);
    }

    @Override
    public Object findDeserializer(Annotated annotated) {
        if (annotated.getRawType()==String.class){
            Decoder decoder = context.getHttpDecoder();
            if (null != decoder){
                return new StdDelegatingDeserializer(decoder,annotated.getType(), StringDeserializer.instance);
            }
        }
        return super.findDeserializer(annotated);
    }

    @Override
    public Object findDeserializationConverter(final Annotated annotated) {
        SecretProperty secretProperty = findSecretProperty(annotated);
        if (null != secretProperty){
            return context.getHttpDecoder(secretProperty.decoderClass(), secretProperty.decoder());
        }
        if (annotated.getRawType()==String.class){
            Decoder decoder = findDecoder(annotated);
            if (null == decoder){
                decoder=context.getHttpDecoder();
            }
            return decoder;
        }
        return super.findDeserializationConverter(annotated);
    }

    private Encoder findEncoder(final Annotated annotated) {
        final AnnotatedElement element = annotated.getAnnotated();
        final Field field = getPropertyField(element);
        if (null != field){
            return context.findEncoderByRule(FindCondition.create(field.getName()));
        }
        return null;
    }

    private Decoder findDecoder(final Annotated annotated) {
        final AnnotatedElement element = annotated.getAnnotated();
        final Field field = getPropertyField(element);
        if (null != field){
            return context.findDecoderByDecoder(FindCondition.create(field.getName()));
        }
        return null;
    }

//    private SecretRule findSecretRule(final Annotated annotated) {
//        final AnnotatedElement element = annotated.getAnnotated();
//        final Field field = getPropertyField(element);
//        if (null != field){
//            return context.findRule(FindCondition.create(field.getName()));
//        }
//        return null;
//    }


    private SecretProperty findSecretProperty(final Annotated annotated) {
        final AnnotatedElement element = annotated.getAnnotated();
        if (element.isAnnotationPresent(SecretProperty.class)) {
            return element.getAnnotation(SecretProperty.class);
        }
        final Field field = getPropertyField(element);
        if (null != field) {
            return field.getAnnotation(SecretProperty.class);
        }
        return null;
    }

    private Field getPropertyField(final AnnotatedElement element) {
        if (element instanceof Method) {
            final Class<?> cls = ((Method) element).getDeclaringClass();
            final String name = getPropertyName(element);
            Field field = null;
            try {
                field = cls.getDeclaredField(name);
            } catch (final Exception e) {
            }
            return field;
        }
        return null;
    }

    private String getPropertyName(final AnnotatedElement element) {
        final String methodName = ((Method) element).getName();
        int index = 0;
        index = methodName.startsWith("is") ? 2 : index;
        index = methodName.startsWith("get") ? 3 : index;
        index = methodName.startsWith("set") ? 3 : index;
        final String fieldName = methodName.substring(index);
        return StringUtils.uncapitalize(fieldName);
    }

}
